<?php

namespace app\ctrl;

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Yurun\Util\YurunHttp\Co\Batch;
use Yurun\Util\HttpRequest;

/**
 * 批量组号，并返回每条结果
 */
class Groupcheck extends BaseController
{

    /**
     * 显示提交域名视图
     */
    public function index()
    {
        // 输出详情html内容
        $view = new \core\lib\View(VIEWS);
        return $view->render('groupcheck', [
            'v' => time(),
            'url' => env('APP_BASEURL') . 'groupcheck/exec',
            'exporturl' => env('APP_BASEURL') . 'groupcheck/export',
            'toolurl' => '/static/dist/组号检查-v1.1.rar?t=' . microtime(true),
        ]);
    }

    /**
     * 导出Excel，根据数据md5值获取缓存数据
     */
    public function export()
    {
        $params = post();
        $this->validate($params, [
            'datamd5' => 'require',
        ]);

        // 获取缓存
        $cacheKey = 'groupcheck:' . $params['datamd5'];
        $redis = new \core\lib\Redis();
        $list = $redis->get($cacheKey);
        if (empty($list)) {
            error('数据已过期，请重新提交并导出');
        }

        // 创建一个新的excel文档
        require_once(VENDOR . '/autoload.php');
        $spreadsheet = new Spreadsheet();
        // 获取当前操作sheet的对象
        $worksheet = $spreadsheet->getActiveSheet();
        // 设置当前sheet的标题
        $worksheet->setTitle('组号查询明细-' . date('Ymd'));
        // 设置宽度为true,不然太窄了
        $spreadsheet->getActiveSheet()->getColumnDimension('A')->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension('B')->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension('C')->setAutoSize(true);

        //设置第一条的标题
        $worksheet->setCellValue('A1', '域名')->setCellValue('B1', '组名')
            ->setCellValue('C1', '结果');

        // 设置粗体
        $spreadsheet->getActiveSheet()->getStyle('A1:C1')->getFont()->setBold(true);

        // 定义颜色
        $colorSelf = '5cb85c';
        $colorOther = 'f0ad4e';
        $colorError = 'd9534f';

        // 开始赋值
        if ($list['items']) {
            foreach ($list['items'] as $k => $item) {
                $i = $k + 2;
                $worksheet->setCellValue("A{$i}", $item['domain'])
                    ->setCellValue("B{$i}", $item['groupname'])
                    ->setCellValue("C{$i}", $item['res']);
                if ($item['isown'] === 1) {
                    $spreadsheet->getActiveSheet()->getStyle("A{$i}:C{$i}")->getFont()->getColor()->setRGB($colorSelf);
                } elseif ($item['code'] === 1) {
                    $spreadsheet->getActiveSheet()->getStyle("A{$i}:C{$i}")->getFont()->getColor()->setRGB($colorOther);
                } else {
                    $spreadsheet->getActiveSheet()->getStyle("A{$i}:C{$i}")->getFont()->getColor()->setRGB($colorError);
                }
            }
            $i = $i + 1;
            $worksheet->setCellValue("C{$i}", '总计：自己组' . $list['stat']['self'] . '个，其他组' . $list['stat']['other'] . '个，劫持失败' . $list['stat']['error'] . '个');
        }

        // 注意这边文件名不能带中文
        $filename = 'groupcheck-' . date('YmdHi');
        $format = 'xls';

        return downloadExcel($spreadsheet, $filename, $format);
    }

    /**
     * 处理批量提交的域名并处理
     */
    public function exec()
    {
        set_time_limit(0);
        $startTime = microtime(true);
        require_once(VENDOR . '/autoload.php');
        $options = [
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
        ];
        $fixnog = '不是自己公司的';
        $maps = [
            '_2' => '林静', '_12' => '林静', '_22' => '林静', '_32' => '林静', '_42' => '林静', '_52' => '林静', '_62' => '林静', '_72' => '林静', '_82' => '林静', '_92' => '林静',
            '_3' => '泉港', '_13' => '泉港', '_23' => '泉港', '_33' => '泉港', '_43' => '泉港', '_53' => '泉港', '_63' => '泉港', '_73' => '泉港', '_83' => '泉港', '_93' => '泉港',
            '_4' => '阿辉', '_14' => '阿辉', '_24' => '阿辉', '_34' => '阿辉', '_44' => '阿辉', '_54' => '阿辉', '_64' => '阿辉', '_74' => '阿辉', '_84' => '阿辉', '_94' => '阿辉',
            '_5' => '林鹏日本', '_15' => '林鹏日本', '_25' => '林鹏日本', '_35' => '林鹏日本', '_45' => '林鹏日本', '_55' => '林鹏日本', '_65' => '林鹏日本', '_75' => '林鹏日本', '_85' => '林鹏日本', '_95' => '林鹏日本',
            '_6' => '林萍', '_16' => '林萍', '_26' => '林萍', '_36' => '林萍', '_46' => '林萍', '_56' => '林萍', '_66' => '林萍', '_76' => '林萍', '_86' => '林萍', '_96' => '林萍',
            '_7' => '佳津', '_17' => '佳津', '_27' => '佳津', '_37' => '佳津', '_47' => '佳津', '_57' => '佳津', '_67' => '佳津', '_77' => '佳津', '_87' => '佳津', '_97' => '佳津',
            '_8' => '超迪', '_18' => '超迪', '_28' => '超迪', '_38' => '超迪', '_48' => '超迪', '_58' => '超迪', '_68' => '超迪', '_78' => '超迪', '_88' => '超迪', '_98' => '超迪',
            '_9' => '林鹏欧美', '_19' => '林鹏欧美', '_29' => '林鹏欧美', '_39' => '林鹏欧美', '_49' => '林鹏欧美', '_59' => '林鹏欧美', '_69' => '林鹏欧美', '_79' => '林鹏欧美', '_89' => '林鹏欧美', '_99' => '林鹏欧美',
            '_a' => '泉港欧美', '_2a' => '泉港欧美', '_3a' => '泉港欧美', '_4a' => '泉港欧美', '_5a' => '泉港欧美', '_6a' => '泉港欧美', '_7a' => '泉港欧美', '_8a' => '泉港欧美', '_9a' => '泉港欧美',
            '_b' => '国银日本老域名', '_2b' => '国银日本老域名', '_3b' => '国银日本老域名', '_4b' => '国银日本老域名', '_5b' => '国银日本老域名', '_6b' => '国银日本老域名', '_7b' => '国银日本老域名', '_8b' => '国银日本老域名', '_9b' => '国银日本老域名',
            '_c' => '超迪欧美', '_2c' => '超迪欧美', '_3c' => '超迪欧美', '_4c' => '超迪欧美', '_5c' => '超迪欧美', '_6c' => '超迪欧美', '_7c' => '超迪欧美', '_8c' => '超迪欧美', '_9c' => '超迪欧美',
            '_d' => '超迪日本老域名', '_2d' => '超迪日本老域名', '_3d' => '超迪日本老域名', '_4d' => '超迪日本老域名', '_5d' => '超迪日本老域名', '_6d' => '超迪日本老域名', '_7d' => '超迪日本老域名', '_8d' => '超迪日本老域名', '_9d' => '超迪日本老域名',
            '_e' => '国银日本外包', '_2e' => '国银日本外包', '_3e' => '国银日本外包', '_4e' => '国银日本外包', '_5e' => '国银日本外包', '_6e' => '国银日本外包', '_7e' => '国银日本外包', '_8e' => '国银日本外包', '_9e' => '国银日本外包',
            '_f' => '泉港意大利', '_2f' => '泉港意大利', '_3f' => '泉港意大利', '_4f' => '泉港意大利', '_5f' => '泉港意大利', '_6f' => '泉港意大利', '_7f' => '泉港意大利', '_8f' => '泉港意大利', '_9f' => '泉港意大利',
            '_g' => '林平欧美', '_2g' => '林平欧美', '_3g' => '林平欧美', '_4g' => '林平欧美', '_5g' => '林平欧美', '_6g' => '林平欧美', '_7g' => '林平欧美', '_8g' => '林平欧美', '_9g' => '林平欧美',
            '_h' => '佳津欧美', '_2h' => '佳津欧美', '_3h' => '佳津欧美', '_4h' => '佳津欧美', '_5h' => '佳津欧美', '_6h' => '佳津欧美', '_7h' => '佳津欧美', '_8h' => '佳津欧美', '_9h' => '佳津欧美',
            '_i' => '超迪ypfz', '_2i' => '超迪ypfz', '_3i' => '超迪ypfz', '_4i' => '超迪ypfz', '_5i' => '超迪ypfz', '_6i' => '超迪ypfz', '_7i' => '超迪ypfz', '_8i' => '超迪ypfz', '_9i' => '超迪ypfz',
            '_j' => '林静欧美', '_2j' => '林静欧美', '_3j' => '林静欧美', '_4j' => '林静欧美', '_5j' => '林静欧美', '_6j' => '林静欧美', '_7j' => '林静欧美', '_8j' => '林静欧美', '_9j' => '林静欧美',
            '_k' => '佳津欧美关键词', '_2k' => '佳津欧美关键词', '_3k' => '佳津欧美关键词', '_4k' => '佳津欧美关键词', '_5k' => '佳津欧美关键词', '_6k' => '佳津欧美关键词', '_7k' => '佳津欧美关键词', '_8k' => '佳津欧美关键词', '_9k' => '佳津欧美关键词',
            '_l' => '林平欧美关键词', '_2l' => '林平欧美关键词', '_3l' => '林平欧美关键词', '_4l' => '林平欧美关键词', '_5l' => '林平欧美关键词', '_6l' => '林平欧美关键词', '_7l' => '林平欧美关键词', '_8l' => '林平欧美关键词', '_9l' => '林平欧美关键词',
            '_m' => '林静欧美关键词', '_2m' => '林静欧美关键词', '_3m' => '林静欧美关键词', '_4m' => '林静欧美关键词', '_5m' => '林静欧美关键词', '_6m' => '林静欧美关键词', '_7m' => '林静欧美关键词', '_8m' => '林静欧美关键词', '_9m' => '林静欧美关键词',
            '_n' => '超迪巴西尾号', '_2n' => '超迪巴西尾号', '_3n' => '超迪巴西尾号', '_4n' => '超迪巴西尾号', '_5n' => '超迪巴西尾号', '_6n' => '超迪巴西尾号', '_7n' => '超迪巴西尾号', '_8n' => '超迪巴西尾号', '_9n' => '超迪巴西尾号',
            '_o' => '超迪另一外包组', '_2o' => '超迪另一外包组', '_3o' => '超迪另一外包组', '_4o' => '超迪另一外包组', '_5o' => '超迪另一外包组', '_6o' => '超迪另一外包组', '_7o' => '超迪另一外包组', '_8o' => '超迪另一外包组', '_9o' => '超迪另一外包组',
            '_p' => '泉港日本低价', '_2p' => '泉港日本低价', '_3p' => '泉港日本低价', '_4p' => '泉港日本低价', '_5p' => '泉港日本低价', '_6p' => '泉港日本低价', '_7p' => '泉港日本低价', '_8p' => '泉港日本低价', '_9p' => '泉港日本低价',
        ];
        $stat = ['self' => 0, 'other' => 0, 'error' => 0];

        $params = post();
        $this->validate($params, [
            'domains' => 'require',
        ]);
        $domains = array_filter(explode("\n", $params['domains']));
        $domains = array_map(function ($v) {
            return trim($v);
        }, $domains);
        $domains = array_unique($domains);
        $sort = !empty($params['sort']) ? true : false;
        $md5 = md5($params['domains']);
        foreach ($domains as $k => $domain) {
            // 组装url
            $url = strtolower($domain);
            if (!str_starts_with($url, 'http')) {
                $url = 'http://' . $url;
            }
            $url = str_replace(' ', '+', $url);
            $temp = $url;

            // retry重试机制：状态码=500或者0时才重试
            $req[] = (new HttpRequest())->options($options)->timeout(6000, 3000)->retry(1)->url($url);
            $urls[] = $url;
            $dms[] = $domain;

            // 找出对应的二级文件名
            $pname = strtolower(basename($domain));
            if (strpos($pname, '.php') !== false) {
                $subphp[$k] = basename($domain);
                if (!str_ends_with($temp, '?sitemap.xml')) {
                    $urls2[$k] = $temp . '?sitemap.xml';
                }
            }
        }

        if (!empty($req)) {
            $gnums = $errors = $errDomains = [];
            $result = Batch::run($req);
            foreach ($result as $k => $resp) {
                $url = $urls[$k];
                $domain = $dms[$k];
                $statusCode = $resp->getStatusCode();
                $data[$k] = [
                    'domain' => $domain,
                    'code' => 0,
                    'endof' => '',
                    'groupname' => '',
                    'isown' => 0,
                    'http_status' => $statusCode,
                ];
                if ($statusCode === 403) {
                    $msg = '访问被拒了';
                    $data[$k]['res'] = $msg;
                    $errDomains[$k] = $domain;
                    continue;
                } elseif ($statusCode === 301) {
                    $msg = '301永久重定向了';
                    $data[$k]['res'] = $msg;
                    $errDomains[$k] = $domain;
                    continue;
                } elseif ($statusCode === 302) {
                    $msg = '302临时重定向了';
                    $data[$k]['res'] = $msg;
                    $errDomains[$k] = $domain;
                    continue;
                } elseif ($statusCode === 404) {
                    $msg = '404网页不存在';
                    $data[$k]['res'] = $msg;
                    $errDomains[$k] = $domain;
                    continue;
                } elseif ($statusCode >= 500) {
                    $msg = $statusCode . '服务端错误了';
                    $data[$k]['res'] = $msg;
                    $errDomains[$k] = $domain;
                    continue;
                }
                $output = strtolower($resp->getBody()->getContents());
                if (is_string($output) && str_starts_with($output, 'ok|') && mb_strlen($output) < 50) {
                    if (str_contains($output, '.php')) {
                        $temp = explode('|', $output);
                        $groupno = $temp[1];
                        $phpname = $temp[2] ?? '';
                        $groupno = substr($groupno, strpos($groupno, '_'));
                        if (strpos($domain, '.php') === false) {
                            $isok = true;
                        } else {
                            $isok = $phpname === 'index.php' && !str_contains($domain, 'index.php') ? false : true;
                            if ($isok) {
                                $isok = $phpname === 'default.php' && !str_contains($domain, 'default.php') ? false : true;
                            }
                        }
                    } else {
                        $isok = true;
                        $groupno = substr($output, strpos($output, '_'));
                        if (isset($subphp[$k]) && !empty($urls2[$k])) {
                            // 旧版劫持代码或者莆田的劫持代码，代表是二级进来的，请求xxx.com/db.php?sitemap.xml
                            $http = new HttpRequest();
                            $response = $http->get($urls2[$k]);
                            $content = $response->body();
                            $isok = str_contains($content, $subphp[$k]) ? true : false;
                        }
                    }
                    if ($isok) {
                        $gnums[$groupno] = isset($gnums[$groupno]) ? ($gnums[$groupno] + 1) : 1;
                        $data[$k]['code'] = 1;
                        $data[$k]['endof'] = $groupno;
                        $data[$k]['groupname'] = isset($maps[$groupno]) ? $maps[$groupno] : $fixnog;
                        $data[$k]['res'] = $output;
                    } else {
                        $data[$k]['res'] = '失败：二级文件已消失';
                        // 针对二级失败的部分，不再请求一次，因为是已知情况的了
                        // $errors[$k] = $url;
                        // $errDomains[$k] = $domain;
                    }
                } else {
                    // $msg = '请求groupcheck链接返回错误';
                    // logger()->error(compact('msg', 'domain', 'output'));

                    if (str_contains($output, 'bandwidth limit exceeded')) {
                        $data[$k]['res'] = '失败：http_status=' . $statusCode . ',output=危险网页，需人工验证';
                    } elseif (str_contains($output, 'ser-agent:') && str_contains($output, 'Allow:')) {
                        $data[$k]['res'] = '失败：http_status=' . $statusCode . ',output=输出xml';
                    } elseif (str_contains($output, 'title>404') || str_contains($output, 'age not found') || str_contains($output, '404 not found')) {
                        $data[$k]['res'] = '失败：http_status=' . $statusCode . ',output=404网页不存在';
                    } elseif ($statusCode !== 500 && str_contains($output, 't500 internal server error')) {
                        $data[$k]['res'] = '失败：http_status=' . $statusCode . ',output=500错误';
                    } else {
                        $data[$k]['res'] = '失败：http_status=' . $statusCode . ',output=请手动排查';
                    }
                    // 针对失败的部分，再次请求一次
                    $errors[$k] = $url;
                    $errDomains[$k] = $domain;
                }
            }
        }

        // 失败重试的，批量
        if (!empty($errors)) {
            for ($i = 1; $i <= 2; $i++) {
                if (empty($errors)) {
                    break;
                }
                $req = [];
                foreach ($errors as $k => $url) {
                    $httpRequest = new HttpRequest();
                    $httpRequest->protocolVersion = '1.1'; // Http 协议版本
                    $options[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36';
                    $options[CURLOPT_REFERER] = $url;
                    $req[$k] = $httpRequest->options($options)->url($url);
                }
                if (!empty($req)) {
                    $result = Batch::run($req);
                    foreach ($result as $k => $resp) {
                        $output = strtolower($resp->getBody()->getContents());
                        if (is_string($output) && str_starts_with($output, 'ok|') && mb_strlen($output) < 20) {
                            $groupno = substr($output, strpos($output, '_'));
                            $gnums[$groupno] = isset($gnums[$groupno]) ? ($gnums[$groupno] + 1) : 1;
                            $data[$k]['code'] = 1;
                            $data[$k]['endof'] = $groupno;
                            $data[$k]['groupname'] = isset($maps[$groupno]) ? $maps[$groupno] : $fixnog;
                            $data[$k]['res'] = $output;
                            unset($errors[$k], $errDomains[$k]);
                        }
                    }
                }
            }
        }

        $otherDomains = [];
        if (empty($gnums)) {
            $stat['error'] = count($data);
        } else {
            // 找出组号最多的为自己组号
            $maxValue = max($gnums);
            $maxKey = current(array_keys($gnums, $maxValue));
            foreach ($data as $k => $item) {
                if ($item['code'] === 1 && $item['endof'] === $maxKey) {
                    $data[$k]['isown'] = 1;
                    $stat['self'] += 1;
                } elseif ($item['code'] === 1 && !empty($item['endof'])) {
                    $otherDomains[] = $item['domain'];
                    $stat['other'] += 1;
                } elseif ($item['code'] === 0) {
                    $stat['error'] += 1;
                }
            }
        }

        // 进行排序
        if ($sort && !empty($gnums)) {
            $last = [];
            foreach ($data as $k => $item) {
                if ($item['code'] === 1 && $item['isown'] === 1) {
                    $last[] = $item;
                    unset($data[$k]);
                }
            }
            foreach ($data as $k => $item) {
                if ($item['code'] === 1 && $item['isown'] === 0) {
                    $last[] = $item;
                    unset($data[$k]);
                }
            }
            foreach ($data as $k => $item) {
                if (in_array($item['http_status'], [301, 302, 403, 404, 500, 502, 503])) {
                    $last[] = $item;
                    unset($data[$k]);
                }
            }
            foreach ($data as $item) {
                $last[] = $item;
            }
            $data = $last;
        }

        $ret = ['stat' => $stat, 'datamd5' => $md5, 'items' => $data];
        // 缓存数据
        $redis = new \core\lib\Redis();
        $cacheKey = 'groupcheck:' . $md5;
        $expireSeconds = 3600;
        $redis->set($cacheKey, json_encode($ret));
        $redis->expire($cacheKey, $expireSeconds);

        // 计算执行时间
        $endTime = microtime(true);
        $ret['stat']['exectime'] = number_format($endTime - $startTime, 2);
        $ret['errdomains'] = !empty($errDomains) ? join("\n", $errDomains) : '';
        $ret['otherdomains'] = !empty($otherDomains) ? join("\n", $otherDomains) : '';

        return $ret;
    }
}